/*string官方内容很多，我们只需要掌握重要的即可*/
#include<iostream>
#include<string>
#include<algorithm>
#include<assert.h>
using namespace std;

void test_string1()
{
    /*基础操作*/
    string s1;

    string s2("hello,world");

    string s3(s2);

    string s4_1(s2,3,5);

    string s4_2(s2,3); 

    //不带后限即从第3个位置开始，后面有多少拷多少；
    string s4_3(s2,3,30);
    //若len>后面字符长度，也是后面有多少拷贝多少
    /*string (const string& str, size_t pos, size_t len = npos);*/
    
    string s5=s4_1+s3;

    return;
}

void mypush_back(const string& s)
{}

void test_string2()
{
    //构造
    string s1("hello world");

    //隐式类型转换 const char[] -> string
    string s2="hello world";

    const string& s3="hello world";

    mypush_back("i am fine"); //隐式类型转换的好处
    
}

class mystring
{
public:
    char& operator[](size_t i)
    {
        assert(i<_size);
        return _str[i];
    }
    /*这里面爆出来了引用返回的第二个意义:
     1、减少拷贝
     2、修改返回对象    
    */
private:
    char* _str;
    size_t _size;
    size_t _capacity;
};

void test_string3()
{
    //遍历
    string s1("hello world");
    cout<<s1.size()<<endl; //为了和其他容器保持一致
    //不算 /0
    cout<<s1.length()<<endl;

    //[]重载  底层如上49行
    //读，且可以写 ->>>>这正是传引用返回的妙处  
    for(size_t i=0;i<s1.size();i++)
    {
        s1[i]='a';
        cout<<s1[i]<<" ";
    }
    cout<<endl;

    //cout<<s1[20]<<endl;  //越界检查灵敏，不像C语言，数组越界读随机检查
}

void test_string4() 
{ 
    //const string s2("hello world");
    // s2[0]='x'; 权限不能放大，只能缩小
    
    string s2("hello world");

    //遍历方式1：下标+[]
    for(size_t i=0;i<s2.size();i++)
    {}

    //遍历方式2：迭代器
    string::iterator it1=s2.begin();
    /*对任何容器而言:
        begin返回第一个位置
        end返回得分是结束位置的下一个位置
    */
    while(it1!=s2.end())
    {
        *it1+=3;
        cout<<*it1<<' ';
        ++it1;
    }
    cout<<endl;

    cout<<typeid(it1).name()<<endl; //迭代器的实现不同编译器实现是不同的
    /*制定标准的人和实现的人不是一批人*/

    //相比之下还是方式1更好用,但迭代器更加通用一些

    //遍历方式3：范围for
    for(auto& e:s2)
    {
        cout<<e<<' ';
    }
    cout<<endl;
    //底层很简单，就是迭代器
     
}

void test_string5()
{
    const string s1("hello world");
    string::const_iterator it1=s1.begin();
    /*若你已经非常清楚是什么类型，可以直接用auto替换*/
    while(it1 != s1.end())
    {
        //*it1+=3;  const迭代器只读
        cout<<*it1<<" ";
        ++it1;
    }
    cout<<endl;
}

void test_string6()
{
    string s2("hello world");
    string::reverse_iterator it2=s2.rbegin();
    while(it2!=s2.rend())
    {
        cout<<*it2<<" ";
        ++it2;  //注意这里是++，反向的++是正向的--
    }
    cout<<endl;

    // const_reverse_iterator 也是无法修改
}

void test_string7()
{
    string s1("hello world");
    cout<<s1<<endl;
    //s1按字典序排序 ->sort，但并不是在string库中，而是在算法库中
    /*sort:规定传入范围左闭右开*/
   
    // sort(s1.begin(),s1.end());
    // cout<<s1<<endl;

    //只排hello
    sort(s1.begin(),s1.begin()+5);
    cout<<s1<<endl;
    return;
}

void test_string8()
{
    string s1;
    s1.push_back('x');
    s1.append("yyyyy!!");
    cout<<s1<<endl;

    s1.clear();

    //但其实最喜欢用；
    s1 += 'x';
    s1 += "yyyy!!";

    cout<<s1<<endl;

    string s2("end");
    s1+=s2;
    cout<<s1<<endl;
}

void test_string9()
{
    //insert 效率不高，实践中不是很常用

    string s1("hello world");
    string s2("hhhh");
    cout<<s1<<endl;
    s1.insert(0,"xxxx"); //在第0个位置
    cout<<s1<<endl;

    //注意：头插一个会出问题->因为没提供这种s1.insert(0,'y');
    //必须要
    s1.insert(0, 1,'y');
    
    //还可以用迭代器 
    s1.insert(s2.begin(),s1.begin(),s1.end());
}

void test_string10()
{
    string s1("hello world");
    
    s1.erase(0,1);//删除第0个位置1个字符
    cout<<s1<<endl;

    s1.erase(5); //没传删几个就把第五个位置后面的全删
    cout<<s1;
}

void test_string11()
{
    //replace效率同样不高，因为需要挪动数据

    string s2("hhhhh");
    s2.replace(5,1,"%20");
    cout<<s2;

    string s2("helo helo helo helo");
    //把所有空格替换成% 这个题就可以直接调replace

}

int main()
{
    test_string11();
    return 0;
}